#include "QxAbstractFactory.hpp"

#include <QDir>
#include <QMap>
#include <QLibrary>
#include <QFileInfo>
#include <QPluginLoader>
#include <QCoreApplication>

class QxAbstractFactory::QxAbstractFactoryPrivate
{
public:

  QxAbstractFactoryPrivate( QxAbstractFactory* me, const QString& pluginDir ) : m_self( me ), m_pluginDir( pluginDir ) {
  }

  ~QxAbstractFactoryPrivate() {
    qDeleteAll( m_plugins );
  }

  void init() {
    QDir dir;
    if ( !getPluginDir( m_pluginDir, &dir ) ) {
      return;
    }

    QStringList plugins = dir.entryList( QDir::Files | QDir::NoSymLinks );
    Q_FOREACH( const QString& fileName, plugins ) {
      QString libName = dir.absoluteFilePath( fileName );
      if( !QLibrary::isLibrary( libName ) ) {
        continue;
      }
      QPluginLoader loader( libName );
      if( loader.load() ) {
        QxAbstractPlugin* plugin = qobject_cast<QxAbstractPlugin*>( loader.instance() );
        if ( plugin ) {
          plugin->setAbsoluteFilename( libName );
          plugin->setParent( m_self );
          m_plugins << plugin;
        }
      }
    }
  }

  bool getPluginDir( const QString& pluginPath, QDir* pluginDir ) {
    QDir appDir( QCoreApplication::applicationDirPath() );

    QStringList dirList;
    dirList << appDir.absolutePath();

    appDir.cdUp();
    dirList << appDir.absolutePath();

    if( !pluginPath.isEmpty() ) {
      Q_FOREACH( const QString& dir, dirList ) {
        QDir pDir( dir + "/plugins/" + pluginPath );
        if( pDir.exists() ) {
          *pluginDir = pDir;
          return true;
        }
      }
    }
    return false;
  }

  QxAbstractFactory*                       m_self;
  QList<QxAbstractPlugin*>                 m_plugins;

  QString                                  m_pluginDir;
  static QMap<QString, QxAbstractFactory*> m_factories;
};

QMap<QString, QxAbstractFactory*> QxAbstractFactory::QxAbstractFactoryPrivate::m_factories;

QxAbstractFactory::~QxAbstractFactory()
{
}

QString QxAbstractFactory::name() const
{
  return QFileInfo(_pd->m_pluginDir).baseName();
}

QList<QxAbstractFactory*> QxAbstractFactory::factories()
{
  return QxAbstractFactoryPrivate::m_factories.values();
}

QxAbstractFactory::QxAbstractFactory( const QString& pluginDir )
  : _pd( new QxAbstractFactoryPrivate( this, pluginDir ) )
{
  _pd->init();
  QxAbstractFactoryPrivate::m_factories[name()] = this;
}

const QList<QxAbstractPlugin*>& QxAbstractFactory::factoryPlugins() const
{
  return _pd->m_plugins;
}
